/**
 * Copyright (c) <2010>, <SUNNY GUPTA>
 * All rights reserved.

 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of the <ORGANIZATION> nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <SUNNY GUPTA> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.gwtp.investmentadvisor.server.externaldataservices.amfi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import com.googlecode.objectify.Key;
import com.gwtp.investmentadvisor.server.core.communication.URLFetchService;
import com.gwtp.investmentadvisor.server.core.exception.NotImplementedException;
import com.gwtp.investmentadvisor.server.core.exception.NotSupportedException;
import com.gwtp.investmentadvisor.server.entities.finance.FinancialSecurity;
import com.gwtp.investmentadvisor.server.entities.finance.FinancialSecurityQuote;
import com.gwtp.investmentadvisor.server.enums.FinancialSecurityType;
import com.gwtp.investmentadvisor.server.externaldataservices.FinancialSecurityDataProvider;
import com.gwtp.investmentadvisor.server.externaldataservices.HistoricalQuotePeriod;
import com.gwtp.investmentadvisor.server.externaldataservices.exception.DataServiceException;
import com.gwtp.investmentadvisor.shared.entities.FinancialSecurityView;
import com.gwtp.investmentadvisor.shared.utilities.Validator;

/**
 * @author Sunny Gupta
 *
 */
public class AMFIDataProvider implements FinancialSecurityDataProvider {
  private static final String AMFI_EXCHANE_NAME = "Association of Mutual Funds in India";
  private static final String AMFI_EXCHANE_ID = "AMFI_MF";

  private static final String LATEST_NAV_ALL_URL = "http://www.amfiindia.com/spages/NAV0.txt";
  private static final String INDIA_CURRENCY_CODE = "INR";
  private static final String HISTORICAL_NAV_SIGNLE_MF = "http://amfiindia.com/NavHistoryReport_Rpt_Po.aspx?rpt=1&";
  
  private static final String MF_CODE_KEY = "mf_code";
  
  public AMFIDataProvider() {
  }

  /* (non-Javadoc)
   * @see com.gwt.investmentadvisor.server.dataservices.FinancialSecurityDataProvider#getCurrentMarketPrice(com.gwt.investmentadvisor.server.entities.FinancialSecurity)
   */
  @Override
  public FinancialSecurityQuote getCurrentMarketPrice(
      FinancialSecurity financialSecurity) throws DataServiceException {
    try {
      throw new NotImplementedException();
    } catch (Exception e) {
      throw new DataServiceException(e);
    }
  }

  /* (non-Javadoc)
   * @see com.gwt.investmentadvisor.server.dataservices.FinancialSecurityDataProvider#getHistoricalQuotes(com.gwt.investmentadvisor.server.entities.FinancialSecurity, java.util.Calendar, java.util.Calendar, com.gwt.investmentadvisor.server.dataservices.HistoricalQuotePeriod)
   */
  @Override
  public List<FinancialSecurityQuote> getHistoricalQuotes(FinancialSecurity financialSecurity,
      Calendar startDate, Calendar endDate,
      HistoricalQuotePeriod quotePeriod) throws DataServiceException {
    try {
      return downloadMFHistoricalQuotes(financialSecurity,
          startDate,
          endDate);
    } catch (Exception e) {
      throw new DataServiceException(e);
    }
  }

  public static String getRegulatorName() {
    return AMFI_EXCHANE_NAME;
  }
  
  /**
   * @param financialSecurity
   * @param startDate
   * @param endDate
   * @return
   * @throws IOException 
   * @throws MalformedURLException 
   */
  private List<FinancialSecurityQuote> downloadMFHistoricalQuotes(FinancialSecurity financialSecurity,
      Calendar startDate, Calendar endDate) throws MalformedURLException, IOException {
    
    // Extract MF code from custom data
    String mfCode = getMFCodeFromFinancialSecurityCustomData(financialSecurity);

    // Date formatter
    SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy");
    
    // Form URL
    String urlString = HISTORICAL_NAV_SIGNLE_MF + 
      "frmdate=" + df.format(startDate.getTime()) + 
      "&todate=" + df.format(endDate.getTime()) + 
      "&mf=" + mfCode +  
      "&scm=" + financialSecurity.getTickerSymbol();
    
    // Obtain data & parse XML    
    String dataString = URLFetchService.urlGet(urlString);
    
    // TODO: Parse XHTML
    /*
    Parser decoder = new XMLDecoder(new BufferedInputStream(url.openStream()));
    
    Object x = decoder.readObject();
    String data = URLFetchService.urlGet(urlString);
    data.length();
    */
    
    throw new NotImplementedException();
    
    // return null;
  }

  private String getMFCodeFromFinancialSecurityCustomData(
      FinancialSecurity financialSecurity) {
    HashMap<String, String> map = financialSecurity.getCustomDataAsHashMap();
    if (map.containsKey(MF_CODE_KEY)) {
      return map.get(MF_CODE_KEY);
    } else {
      return "";
    }
  }

  /* (non-Javadoc)
   * @see com.gwt.investmentadvisor.server.dataservices.FinancialSecurityDataProvider#searchByText(java.lang.String)
   */
  @Override
  public List<FinancialSecurityView> searchByText(String searchText)
      throws DataServiceException {
    try {
      throw new NotImplementedException();
    } catch (Exception e) {
      throw new DataServiceException(e);
    }
  }
  
  /*
   * Gets the raw data from AMFI and parses to give complete list of all MFs with their latest
   * NAVs
   */
  private List<FinancialSecurityQuote> downloadLatestNAVForAllFunds() throws MalformedURLException, IOException, ParseException {
    // Form URL
    URL url = new URL(LATEST_NAV_ALL_URL);
    // Read each line & parse
    BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
    
    // Initialize List
    List<FinancialSecurityQuote> quotesList = new ArrayList<FinancialSecurityQuote>();
    String line;
    while ((line = reader.readLine()) != null) {
      // Starts with numeric scheme code...& contains ;
      if (line.contains(";")) {
        String[] parts = line.split(";");
        String tickerSymbol = parts[0];
        // First part should be pure integer
        if (Validator.isPositiveInteger(tickerSymbol)) {
          // Amount in Indian Currency
          BigDecimal price = new BigDecimal(parts[2]);

          // Parse Date
          SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy");
          Date date = df.parse(parts[5]);
          
          // Obtain Financial Security Unique Identifier
          String financialSecurityUniqueIdentifier = 
            FinancialSecurity.createFinancialSecurityUniqueIdentifier(
                getAMFIMarketID(), 
                FinancialSecurityType.MUTUAL_FUND, 
                tickerSymbol, getCustomData(parts));
          
          // Generate Quote
          FinancialSecurityQuote quote = new FinancialSecurityQuote(
              new Key<FinancialSecurity>(FinancialSecurity.class, financialSecurityUniqueIdentifier), 
              price,
              date);
          
          // Add to list
          quotesList.add(quote);
        }
      }
    }
    // Close stream
    reader.close();
    
    // return
    return quotesList;
  }

  private String getCustomData(String[] parts) {
    return MF_CODE_KEY + "=" + parts[0];
  }

  private String getAMFIMarketID() {
    // TODO Auto-generated method stub
    return AMFI_EXCHANE_ID;
  }

  /* (non-Javadoc)
   * @see com.gwt.investmentadvisor.server.dataservices.FinancialSecurityDataProvider#getCurrentMarketPriceForAllFinancialSecurities()
   */
  @Override
  public List<FinancialSecurityQuote> getCurrentMarketPriceForAllFinancialSecurities()
      throws DataServiceException, NotSupportedException {
    try {
      return downloadLatestNAVForAllFunds();
    } catch (Exception e) {
      throw new DataServiceException(e);
    }
  }

  @Override
  public FinancialSecurityView getCurrentMarketPrice(
      FinancialSecurityView financialSecurityView)
      throws DataServiceException {
    try {
      throw new NotImplementedException();
    } catch (Exception e) {
      throw new DataServiceException(e);
    }
  }

}
